<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>协同过滤算法演示</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
        }
        .explanation {
            margin: 20px 0;
            padding: 15px;
            background-color: #eef;
            border-radius: 5px;
            border-left: 4px solid #66c;
        }
        .matrix {
            display: flex;
            flex-direction: column;
            margin: 20px 0;
        }
        .matrix-row {
            display: flex;
        }
        .matrix-cell {
            width: 60px;
            height: 60px;
            border: 1px solid #ddd;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            transition: all 0.5s ease;
        }
        .matrix-header {
            background-color: #f0f0f0;
            font-weight: bold;
        }
        .user-cell {
            background-color: #e6f7ff;
        }
        .item-cell {
            background-color: #f6ffed;
        }
        .rating {
            font-size: 18px;
        }
        .similar-user {
            background-color: #fffb8f;
            animation: highlight 1.5s infinite;
        }
        .predicted {
            background-color: #b7eb8f;
            animation: pulse 1.5s infinite;
        }
        .controls {
            text-align: center;
            margin: 20px 0;
        }
        button {
            padding: 8px 16px;
            background-color: #1890ff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin: 0 5px;
        }
        button:hover {
            background-color: #40a9ff;
        }
        @keyframes highlight {
            0% { background-color: #fffb8f; }
            50% { background-color: #fff566; }
            100% { background-color: #fffb8f; }
        }
        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.1); }
            100% { transform: scale(1); }
        }
        .step {
            display: none;
        }
        .active {
            display: block;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>协同过滤算法演示</h1>
        
        <div class="explanation">
            <p>协同过滤是一种推荐算法，通过分析用户的历史行为数据，找到相似的用户或物品，然后基于这些相似性进行推荐。</p>
            <p>本演示展示基于用户的协同过滤(User-based CF)过程：</p>
            <ol>
                <li>计算用户之间的相似度</li>
                <li>找到与目标用户最相似的用户</li>
                <li>基于相似用户的评分预测目标用户对未评分物品的评分</li>
            </ol>
        </div>
        
        <div class="controls">
            <button id="prevBtn" disabled>上一步</button>
            <button id="nextBtn">下一步</button>
            <button id="resetBtn">重置</button>
        </div>
        
        <div id="step1" class="step active">
            <h3>步骤1: 初始评分矩阵</h3>
            <p>我们有一个用户-物品评分矩阵，其中一些评分为空(?)表示用户尚未对该物品评分。</p>
            <div class="matrix" id="initialMatrix"></div>
        </div>
        
        <div id="step2" class="step">
            <h3>步骤2: 计算用户相似度</h3>
            <p>计算目标用户(User1)与其他用户的相似度(使用余弦相似度)。</p>
            <div class="matrix" id="similarityMatrix"></div>
        </div>
        
        <div id="step3" class="step">
            <h3>步骤3: 找到相似用户</h3>
            <p>选择与目标用户(User1)最相似的用户(User3)。</p>
            <div class="matrix" id="similarUserMatrix"></div>
        </div>
        
        <div id="step4" class="step">
            <h3>步骤4: 预测缺失评分</h3>
            <p>基于相似用户(User3)的评分预测目标用户(User1)对Item3的评分。</p>
            <div class="matrix" id="predictionMatrix"></div>
            <p>预测评分 = 相似用户的评分 = 5</p>
        </div>
    </div>

    <script>
        // 初始数据
        const users = ['User1', 'User2', 'User3', 'User4'];
        const items = ['Item1', 'Item2', 'Item3', 'Item4'];
        const ratings = [
            [5, 3, null, 1],  // User1
            [4, null, 4, 2],  // User2
            [1, 1, 5, null],  // User3
            [1, null, 5, 4]   // User4
        ];
        
        // 计算余弦相似度
        function cosineSimilarity(a, b) {
            let dotProduct = 0;
            let normA = 0;
            let normB = 0;
            
            for (let i = 0; i < a.length; i++) {
                if (a[i] !== null && b[i] !== null) {
                    dotProduct += a[i] * b[i];
                    normA += a[i] * a[i];
                    normB += b[i] * b[i];
                }
            }
            
            if (normA === 0 || normB === 0) return 0;
            return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
        }
        
        // 计算所有用户与User1的相似度
        function calculateSimilarities() {
            const targetUser = 0; // User1
            const similarities = [];
            
            for (let i = 0; i < users.length; i++) {
                if (i === targetUser) {
                    similarities.push(1); // 自己与自己的相似度为1
                } else {
                    similarities.push(cosineSimilarity(ratings[targetUser], ratings[i]).toFixed(2));
                }
            }
            
            return similarities;
        }
        
        // 渲染初始矩阵
        function renderInitialMatrix() {
            const matrixDiv = document.getElementById('initialMatrix');
            matrixDiv.innerHTML = '';
            
            // 表头
            const headerRow = document.createElement('div');
            headerRow.className = 'matrix-row';
            
            const emptyCell = document.createElement('div');
            emptyCell.className = 'matrix-cell matrix-header';
            emptyCell.textContent = '';
            headerRow.appendChild(emptyCell);
            
            items.forEach(item => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell matrix-header item-cell';
                cell.textContent = item;
                headerRow.appendChild(cell);
            });
            
            matrixDiv.appendChild(headerRow);
            
            // 数据行
            users.forEach((user, userIndex) => {
                const row = document.createElement('div');
                row.className = 'matrix-row';
                
                const userCell = document.createElement('div');
                userCell.className = 'matrix-cell matrix-header user-cell';
                userCell.textContent = user;
                row.appendChild(userCell);
                
                ratings[userIndex].forEach((rating, itemIndex) => {
                    const cell = document.createElement('div');
                    cell.className = 'matrix-cell';
                    cell.id = `cell-${userIndex}-${itemIndex}`;
                    
                    if (rating !== null) {
                        cell.innerHTML = `<span class="rating">${rating}</span>`;
                    } else {
                        cell.innerHTML = '<span class="rating">?</span>';
                    }
                    
                    row.appendChild(cell);
                });
                
                matrixDiv.appendChild(row);
            });
        }
        
        // 渲染相似度矩阵
        function renderSimilarityMatrix() {
            const matrixDiv = document.getElementById('similarityMatrix');
            matrixDiv.innerHTML = '';
            
            const similarities = calculateSimilarities();
            
            // 表头
            const headerRow = document.createElement('div');
            headerRow.className = 'matrix-row';
            
            const emptyCell = document.createElement('div');
            emptyCell.className = 'matrix-cell matrix-header';
            emptyCell.textContent = '';
            headerRow.appendChild(emptyCell);
            
            const similarityCell = document.createElement('div');
            similarityCell.className = 'matrix-cell matrix-header';
            similarityCell.textContent = '与User1相似度';
            headerRow.appendChild(similarityCell);
            
            matrixDiv.appendChild(headerRow);
            
            // 数据行
            users.forEach((user, userIndex) => {
                const row = document.createElement('div');
                row.className = 'matrix-row';
                
                const userCell = document.createElement('div');
                userCell.className = 'matrix-cell matrix-header user-cell';
                userCell.textContent = user;
                row.appendChild(userCell);
                
                const similarityValueCell = document.createElement('div');
                similarityValueCell.className = 'matrix-cell';
                similarityValueCell.textContent = similarities[userIndex];
                row.appendChild(similarityValueCell);
                
                matrixDiv.appendChild(row);
            });
        }
        
        // 渲染相似用户矩阵
        function renderSimilarUserMatrix() {
            const matrixDiv = document.getElementById('similarUserMatrix');
            matrixDiv.innerHTML = '';
            
            // 表头
            const headerRow = document.createElement('div');
            headerRow.className = 'matrix-row';
            
            const emptyCell = document.createElement('div');
            emptyCell.className = 'matrix-cell matrix-header';
            emptyCell.textContent = '';
            headerRow.appendChild(emptyCell);
            
            items.forEach(item => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell matrix-header item-cell';
                cell.textContent = item;
                headerRow.appendChild(cell);
            });
            
            matrixDiv.appendChild(headerRow);
            
            // 数据行 - User1
            const user1Row = document.createElement('div');
            user1Row.className = 'matrix-row';
            
            const user1Cell = document.createElement('div');
            user1Cell.className = 'matrix-cell matrix-header user-cell';
            user1Cell.textContent = 'User1';
            user1Row.appendChild(user1Cell);
            
            ratings[0].forEach((rating, itemIndex) => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell';
                
                if (rating !== null) {
                    cell.innerHTML = `<span class="rating">${rating}</span>`;
                } else {
                    cell.innerHTML = '<span class="rating">?</span>';
                }
                
                user1Row.appendChild(cell);
            });
            
            matrixDiv.appendChild(user1Row);
            
            // 数据行 - User3 (最相似用户)
            const user3Row = document.createElement('div');
            user3Row.className = 'matrix-row';
            
            const user3Cell = document.createElement('div');
            user3Cell.className = 'matrix-cell matrix-header user-cell similar-user';
            user3Cell.textContent = 'User3 (最相似)';
            user3Row.appendChild(user3Cell);
            
            ratings[2].forEach((rating, itemIndex) => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell';
                if (itemIndex === 2) { // Item3
                    cell.classList.add('similar-user');
                }
                
                if (rating !== null) {
                    cell.innerHTML = `<span class="rating">${rating}</span>`;
                } else {
                    cell.innerHTML = '<span class="rating">?</span>';
                }
                
                user3Row.appendChild(cell);
            });
            
            matrixDiv.appendChild(user3Row);
        }
        
        // 渲染预测矩阵
        function renderPredictionMatrix() {
            const matrixDiv = document.getElementById('predictionMatrix');
            matrixDiv.innerHTML = '';
            
            // 表头
            const headerRow = document.createElement('div');
            headerRow.className = 'matrix-row';
            
            const emptyCell = document.createElement('div');
            emptyCell.className = 'matrix-cell matrix-header';
            emptyCell.textContent = '';
            headerRow.appendChild(emptyCell);
            
            items.forEach(item => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell matrix-header item-cell';
                cell.textContent = item;
                headerRow.appendChild(cell);
            });
            
            matrixDiv.appendChild(headerRow);
            
            // 数据行 - User1
            const user1Row = document.createElement('div');
            user1Row.className = 'matrix-row';
            
            const user1Cell = document.createElement('div');
            user1Cell.className = 'matrix-cell matrix-header user-cell';
            user1Cell.textContent = 'User1';
            user1Row.appendChild(user1Cell);
            
            ratings[0].forEach((rating, itemIndex) => {
                const cell = document.createElement('div');
                cell.className = 'matrix-cell';
                
                if (itemIndex === 2) { // Item3的预测评分
                    cell.classList.add('predicted');
                    cell.innerHTML = '<span class="rating">5</span>';
                } else if (rating !== null) {
                    cell.innerHTML = `<span class="rating">${rating}</span>`;
                } else {
                    cell.innerHTML = '<span class="rating">?</span>';
                }
                
                user1Row.appendChild(cell);
            });
            
            matrixDiv.appendChild(user1Row);
        }
        
        // 步骤控制
        let currentStep = 1;
        const totalSteps = 4;
        
        function updateStepDisplay() {
            // 隐藏所有步骤
            document.querySelectorAll('.step').forEach(step => {
                step.classList.remove('active');
            });
            
            // 显示当前步骤
            document.getElementById(`step${currentStep}`).classList.add('active');
            
            // 更新按钮状态
            document.getElementById('prevBtn').disabled = currentStep === 1;
            document.getElementById('nextBtn').disabled = currentStep === totalSteps;
            
            // 根据步骤渲染相应的内容
            switch(currentStep) {
                case 1:
                    renderInitialMatrix();
                    break;
                case 2:
                    renderSimilarityMatrix();
                    break;
                case 3:
                    renderSimilarUserMatrix();
                    break;
                case 4:
                    renderPredictionMatrix();
                    break;
            }
        }
        
        // 事件监听
        document.getElementById('nextBtn').addEventListener('click', () => {
            if (currentStep < totalSteps) {
                currentStep++;
                updateStepDisplay();
            }
        });
        
        document.getElementById('prevBtn').addEventListener('click', () => {
            if (currentStep > 1) {
                currentStep--;
                updateStepDisplay();
            }
        });
        
        document.getElementById('resetBtn').addEventListener('click', () => {
            currentStep = 1;
            updateStepDisplay();
        });
        
        // 初始化
        updateStepDisplay();
    </script>
</body>
</html>